﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using SharpShare.Storage.Security;

namespace SharpShare.Storage.ApplePhotoLibrary {
    public sealed class PhotoLibraryStorageProvider : StorageContainer, IStorageProvider {
        private object _lock = new object();
        private DateTime _libraryReadModifiedDate;

        public PhotoLibraryStorageProvider(string name, IStorageContainer librarySource)
            : base(name, null) {

            this.LibrarySource = librarySource;
            this.Library = new PhotoLibrary();

            IStorageProvider provider = this.LibrarySource.GetStorageProvider();
            if (provider != null) {
                provider.ContentsChanged += new EventHandler(LibrarySourceProviderContentsChanged);
            }
        }

        public IStorageContainer LibrarySource { get; private set; }
        public PhotoLibrary Library { get; private set; }

        public override string ToString() {
            return string.Format("[iPhoto Library] {0}", this.LibrarySource);
        }

        protected override void OnAccessingContents() {
            this.Update();
        }

        private void Update() {
            lock (_lock) {
                if (!RefreshLibraryIfNecessary())
                    return;

                this.Contents.Clear();

                // Populate all photos
                StorageContainer photosContainer = new StorageContainer("Photos", this) {
                    AvailableAccess = -StorageAccessList.Modification
                };

                foreach (Image image in this.Library.Images) {
                    StorageFile file = image.FileFromImage(photosContainer, this.LibrarySource);
                    if (file != null) {
                        photosContainer.Contents.Add(file);
                    }
                }

                // Populate faces
                StorageContainer facesContainer = new StorageContainer("Faces", this) {
                    AvailableAccess = -StorageAccessList.Modification
                };

                foreach (Face face in this.Library.Faces) {
                    StorageContainer faceContainer;

                    if (facesContainer.Contents.Contains(face.Name)) {
                        faceContainer = (StorageContainer)facesContainer.Contents[face.Name];
                    } else {
                        faceContainer = new StorageContainer(face.Name, facesContainer) {
                            AvailableAccess = -StorageAccessList.Modification
                        };

                        facesContainer.Contents.Add(faceContainer);
                    }

                    var imagesWithFace = this.Library.Images
                        .Where(i => i.Faces.Any(f => f.FaceKey == face.Key));

                    foreach (Image image in imagesWithFace) {
                        StorageFile file = image.FileFromImage(faceContainer, this.LibrarySource);
                        if (file != null) {
                            faceContainer.Contents.Add(file);
                        }
                    }
                }

                // Remove folders with no pictures
                var emptyFaces = facesContainer.Contents
                    .Select(c => c.DemandContainer())
                    .Where(c => !c.Contents.Any())
                    .ToList();

                foreach (IStorageContainer emptyFace in emptyFaces) {
                    facesContainer.Contents.Remove(emptyFace);
                }

                // Add to contents
                this.Contents.Add(photosContainer);
                this.Contents.Add(facesContainer);
            }
        }
        private bool RefreshLibraryIfNecessary() {
            lock (_lock) {
                if (_libraryReadModifiedDate != this.LibrarySource.DateModified) {
                    this.RefreshLibrary();
                    return true;
                }
            }

            return false;
        }
        private void RefreshLibrary() {
            lock (_lock) {
                try {
                    this.Library.Read(this.LibrarySource);
                    _libraryReadModifiedDate = this.LibrarySource.DateModified;
                } catch {

                }
            }
        }

        #region IStorageProvider Members

        public event EventHandler ContentsChanged;

        public ulong AvailableBytes {
            get { return 0; }
        }

        public ulong TotalBytes {
            get {
                IStorageProvider provider = this.LibrarySource.GetStorageProvider();
                if (provider == null)
                    return 0;
                return provider.TotalBytes;
            }
        }

        public Searching.IStorageSearchEngine SearchEngine {
            get { return null; }
        }

        #endregion

        private void LibrarySourceProviderContentsChanged(object sender, EventArgs e) {
            lock (_lock) {
                if (_libraryReadModifiedDate != this.LibrarySource.DateModified) {
                    this.OnContentsChanged();
                }
            }
        }

        private void OnContentsChanged() {
            if (ContentsChanged != null) {
                try {
                    ContentsChanged(this, EventArgs.Empty);
                } catch { }
            }
        }
    }
}
